extern crate pest;
extern crate rustyline;

#[macro_use]
extern crate pest_derive;

use pest::Parser;
use rustyline::error::ReadlineError;
use rustyline::Editor;


// Tell Rust, that the struct below shall be created from the grammar
// in the referenced file.
#[derive(Parser)]
#[grammar = "grammar.pest"]
pub struct LispyParser;


const HISTORY_FILENAME: &str = "history.txt";
const PROMPT: &str = "LISPY>> ";


fn print_initial_info() {
    println!("Lispy Version 0.0.1");
    println!("Press Ctrl+c to Exit.\n");
}


fn repl(mut readline_editor: Editor<()>) {
    loop {
        let readline = readline_editor.readline(PROMPT);
        match readline {
            Ok(line) => {
                readline_editor.add_history_entry(line.as_ref());
                match LispyParser::parse(Rule::lispy, &line) {
                    Ok(parsing_result) => {
                        println!("Parsing OK: {}", parsing_result);
                    },
                    Err(some_err) => {
                        println!("There was an error while parsing: {:#?}", some_err);
                    }
                }
            },
            Err(ReadlineError::Interrupted) => {
                println!("CTRL-C");
                break
            },
            Err(ReadlineError::Eof) => {
                println!("CTRL-D");
                break
            },
            Err(err) => {
                println!("Error: {:?}", err);
                break
            }
        }
        readline_editor
            .save_history(HISTORY_FILENAME)
            .expect("Could not save to readline history.");
    }
}


fn main() {
    // We create an editor for the readline history.
    let mut readline_editor = Editor::<()>::new();
    // And then load the history, if it exists.
    if readline_editor.load_history(HISTORY_FILENAME).is_err() {
        println!("No previous history.");
    }
    print_initial_info();
    repl(readline_editor);
}

#[cfg(test)]
mod tests {
    // Quote: "values being compared must implement the PartialEq and Debug traits"
    // use super::*;

    #[test]
    fn it_asserts() {
        assert!(2 + 2 == 4);
    }

    #[test]
    fn it_asserts_with_failure_message() {
        assert!(2 + 2 == 5,
                "Whoot? Where did I go wrong? I thought {} == {}.",
                "2 + 2", "5");
    }

    #[test]
    fn it_assert_eqs() {
        assert_eq!(2 + 2, 4);
    }

    #[test]
    fn it_assert_nes() {
        assert_ne!(2 + 2, 0);
    }

    #[test]
    fn this_fails() {
        panic!("I am failing!");
    }

    #[test]
    #[should_panic]
    fn this_is_expected_to_fail() {
        // Even though the assertion fails, the test passes, because
        // the assertion is expected to fail!
        assert_eq!(1 + 1, 3);
    }

    #[test]
    #[should_panic(expected = "No, 1 + 1 is not 3!")]
    fn this_is_expected_to_fail_with_failure_message() {
        // Even though the assertion fails, the test passes, because
        // the assertion is expected to fail!
        assert_eq!(1 + 1, 3, "No, {} is not {}!", "1 + 1", "3");
    }

    // We can also use the Result type for tests as follows:
    // Quote: "You can't use the #[should_panic] annotation on tests
    // that use Result<T, E>. Instead, you should return an Err value
    // directly when the test should fail."
    #[test]
    fn it_works() -> Result<(), String> {
        if 2 + 2 == 4 {
            Ok(())
        } else {
            Err(String::from("two plus two does not equal four"))
        }
    }
}
